/**
 * @file model_base.h
 *
 * The base infrastructure for modeling
 *
 * @author Dahua Lin
 */

#ifdef _MSC_VER
#pragma once
#endif

#ifndef GML_MODEL_BASE_H_
#define GML_MODEL_BASE_H_

#include <gmlpp/base/basic_defs.h>
#include <gmlpp/base/arma.h>
#include <string>
#include <vector>

namespace gml
{
	// forward declaration

	class FactorSlotBase;

	class FactorBase;

	class VariableBase;


	/********************************************
	 *
	 *   Base classes
	 *
	 ********************************************/

	class FactorSlotBase
	{
	protected:
		FactorSlotBase() { }

		virtual ~FactorSlotBase() { }

	public:
		virtual const FactorBase& host() const = 0;

		virtual FactorBase& host() = 0;

		virtual void set_var(const VariableBase& var) = 0;

		virtual const VariableBase* ptr_var() const = 0;

		// call this function upon the update of the variable
		virtual void on_var_updated() = 0;

	}; // end class FactorSlotBase



	class FactorBase
	{
	protected:
		FactorBase(const char *name)
		: m_name(name)
		{
		}

		FactorBase(const std::string& name)
		: m_name(name)
		{
		}

		virtual ~FactorBase() { }

	public:
		const char *name() const
		{
			return m_name.c_str();
		}

	public:
		virtual size_t num_slots() const = 0;

		virtual double evaluate() = 0;

	private:
		const std::string m_name;

	}; // end class FactorBase


	class VariableBase
	{
		friend void connect(FactorSlotBase& slot, VariableBase& var);

	protected:
		VariableBase(const char *name)
		: m_name(name)
		{
		}

		VariableBase(const std::string& name)
		: m_name(name)
		{
		}

		virtual ~VariableBase() { }

	public:
		const char *name() const
		{
			return m_name.c_str();
		}

		size_t num_conn_slots() const
		{
			return m_conn_slots.size();
		}

	protected:
		void notify_update()
		{
			typedef std::vector<FactorSlotBase*>::iterator iter_t;

			for (iter_t it = m_conn_slots.begin(); it != m_conn_slots.end(); ++it)
			{
				FactorSlotBase* ps = *it;
				ps->on_var_updated();
			}
		}

	private:
		const std::string m_name;
		std::vector<FactorSlotBase*> m_conn_slots;

	}; // end class VariableBase


	inline void connect(FactorSlotBase& slot, VariableBase& var)
	{
		slot.set_var(var);
		var.m_conn_slots.push_back(&slot);
	}


	/********************************************
	 *
	 *  Useful variable classes
	 *
	 ********************************************/


	class VecVariable : public VariableBase
	{
	public:
		VecVariable(uword d)
		: m_data(d, 1)
		{
		}

		VecVariable(uword d, uword n)
		: m_data(d, n)
		{
		}

		VecVariable(mat& dat)
		: m_data(dat)
		{
		}

		GML_ENSURE_INLINE const mat& data() const
		{
			return m_data;
		}

		GML_ENSURE_INLINE mat& data()
		{
			return m_data;
		}

		GML_ENSURE_INLINE uword dim() const
		{
			return m_data.n_rows;
		}

		GML_ENSURE_INLINE uword num() const
		{
			return m_data.n_cols;
		}

		GML_ENSURE_INLINE void zeros()
		{
			m_data.zeros();
		}

	private:
		mat m_data;

	}; // end class VecVariable


}

#endif /* MODEL_BASE_H_ */
